package cn.changeforyou.web.dao.mybatis.ext.extBuilder.builder;

import cn.changeforyou.web.dao.mybatis.ext.extBuilder.SqlContext;
import cn.changeforyou.web.dao.mybatis.ext.metaInfo.ConditionCombineFieldMetaInfo;
import cn.changeforyou.web.dao.mybatis.ext.metaInfo.ConditionFieldMetaInfo;
import cn.changeforyou.web.dao.mybatis.ext.metaInfo.ConditionMetaInfo;
import cn.changeforyou.web.dao.mybatis.ext.metaInfo.FieldMetaInfo;
import cn.changeforyou.web.dao.mybatis.ext.metaInfo.condition.CombineAnnotationEnum;
import cn.changeforyou.web.utils.tree.Tree;
import cn.changeforyou.web.utils.tree.TreeNode;
import cn.changeforyou.web.utils.tree.TreeUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zhyu
 * @version 1.0
 * @date 2022/3/26 19:06
 */

public class WhereBuildUtil {
    public static final String PARENT_ID = "0";
    public static final String FIELD_ID_PREFIX = "#";
    public static final String META_INFO = "meta_info";

    /**
     * 根据fieldMetaInfoList构建where树
     *
     * @param fieldMetaInfos
     * @param sqlContext
     */
    public static void buildWhereTreeByFieldMetaInfoList(List<FieldMetaInfo> fieldMetaInfos, SqlContext sqlContext) {
        //解析ConditionFieldMetaInfo(去掉空的)
        List<TreeNode<String>> treeNodeList = new ArrayList<>();
        for (FieldMetaInfo fieldMetaInfo : fieldMetaInfos) {
            TreeNode<String> node = new TreeNode<>();
            treeNodeList.add(node);
            Map<String, Object> map = new HashMap<>();
            node.setExtra(map);

            map.put(META_INFO, fieldMetaInfo);

            node.setName(fieldMetaInfo.getFieldName());
            node.setParentId(PARENT_ID);
            node.setId(FIELD_ID_PREFIX + fieldMetaInfo.getFieldName());
        }
        Tree<String> trees = TreeUtil.buildSingle(treeNodeList, "0");
        sqlContext.setWhereTrees(trees);
    }

    /**
     * 根据ConditionMetaInfo构建where树
     *
     * @param conditionMetaInfo
     * @param sqlContext
     */
    public static void buildWhereTreeByConditionMetaInfo(ConditionMetaInfo conditionMetaInfo, SqlContext sqlContext) {
        List<TreeNode<String>> treeNodeList = new ArrayList<>();
        buildConditionFieldMetaInfoIntoTree(conditionMetaInfo, treeNodeList);

        //解析ConCombineFieldMetaInfo中的between
        buildConditionCombineFieldMetaInfoIntoTree(conditionMetaInfo, treeNodeList, sqlContext);

        //成树
        Tree<String> trees = TreeUtil.buildSingleInChildrenMode(treeNodeList, "0");
        sqlContext.setWhereTrees(trees);
    }

    /**
     * 构建ConditionFieldMetaInfo到树
     *
     * @param conditionMetaInfo
     * @param treeNodeList
     */
    private static void buildConditionFieldMetaInfoIntoTree(ConditionMetaInfo conditionMetaInfo, List<TreeNode<String>> treeNodeList) {
        List<ConditionFieldMetaInfo> conditionFieldMetaInfoList = conditionMetaInfo.getConditionFieldMetaInfoList();
        for (ConditionFieldMetaInfo metaInfo : conditionFieldMetaInfoList) {
            TreeNode<String> node = new TreeNode<>();
            treeNodeList.add(node);
            Map<String, Object> map = new HashMap<>();
            node.setExtra(map);
            node.setId(metaInfo.getKey());
            node.setName(metaInfo.getFieldName());

            map.put(META_INFO, metaInfo);
        }
    }

    /**
     * 构建ConditionCombineFieldMetaInfo入树
     *
     * @param conditionMetaInfo
     * @param treeNodeList
     * @param sqlContext
     */
    private static void buildConditionCombineFieldMetaInfoIntoTree(ConditionMetaInfo conditionMetaInfo, List<TreeNode<String>> treeNodeList, SqlContext sqlContext) {
        List<ConditionCombineFieldMetaInfo> combineFieldMetaInfoList =
                conditionMetaInfo.getConditionCombineFieldMetaInfoList();
        List<ConditionCombineFieldMetaInfo> brackets = new ArrayList<>();
        List<ConditionCombineFieldMetaInfo> betweens = new ArrayList<>();
        for (ConditionCombineFieldMetaInfo metaInfo : combineFieldMetaInfoList) {
            boolean or = metaInfo.isOr();
            if (or && !sqlContext.isHasOr()) {
                sqlContext.setHasOr(true);
            }
            if (metaInfo.getType() == CombineAnnotationEnum.between) {
                betweens.add(metaInfo);
            } else if (metaInfo.getType() == CombineAnnotationEnum.bracketWrapper) {
                brackets.add(metaInfo);
            }
        }
        if (!brackets.isEmpty()) {
            sqlContext.setHasBracket(true);
        }
        //构建between
        buildBetweenIntoTree(treeNodeList, betweens);

        //解析bracket
        buildBracketIntoTree(treeNodeList, brackets);
    }

    /**
     * 构建between
     *
     * @param treeNodeList
     * @param betweens
     */
    private static void buildBetweenIntoTree(List<TreeNode<String>> treeNodeList, List<ConditionCombineFieldMetaInfo> betweens) {
        for (ConditionCombineFieldMetaInfo fieldMetaInfo : betweens) {
            TreeNode<String> node = new TreeNode<>();
            treeNodeList.add(node);
            Map<String, Object> map = new HashMap<>();
            node.setExtra(map);

            List<String> childrenIds = new ArrayList<>();
            map.put(META_INFO, fieldMetaInfo);

            childrenIds.add(fieldMetaInfo.getLeftBetween());
            childrenIds.add(fieldMetaInfo.getRightAnd());
            childrenIds.add(fieldMetaInfo.getLeftAnd());
            node.setWeight(fieldMetaInfo.isOr() ? 1 : 0);
            node.setChildrenIds(childrenIds);
            node.setName(fieldMetaInfo.getKey());
            node.setId(fieldMetaInfo.getKey());
            node.setParentId(fieldMetaInfo.getParentKey());
        }
    }

    /**
     * 构建括号
     *
     * @param treeNodeList
     * @param brackets
     */
    private static void buildBracketIntoTree(List<TreeNode<String>> treeNodeList, List<ConditionCombineFieldMetaInfo> brackets) {
        for (ConditionCombineFieldMetaInfo fieldMetaInfo : brackets) {
            TreeNode<String> node = new TreeNode<>();
            treeNodeList.add(node);
            Map<String, Object> map = new HashMap<>();
            node.setExtra(map);

            List<String> childrenIds = new ArrayList<>();
            map.put(META_INFO, fieldMetaInfo);
            for (String conditionKey : fieldMetaInfo.getConditionKeys()) {
                childrenIds.add(conditionKey);
            }

            node.setWeight(fieldMetaInfo.isOr() ? 1 : 0);
            node.setChildrenIds(childrenIds);
            node.setName(fieldMetaInfo.getKey());
            node.setId(fieldMetaInfo.getKey());
            node.setParentId(fieldMetaInfo.getParentKey());
        }
    }

}
